/**
 * 视频解码器 h264_mediacodec FFMpeg6.0引入Android MediaCodec
 * Java层MediaCodec：
 *  MediaCodec主要用于视频解码和编码操作，可以实现视频倍速播放、全关键帧转换、视频倒放等功能
 *  1.工作原理
 *          1.获取一块空闲的输入缓存                                               3.获取一个处理后的数据
 *          dequeueInputBuffer                                                  dequeueOutputBuffer
 *          getInputBuffer                                                      getOutputBuffer
 *      Buffer <--------------> InputBuffer -> Decoder/Encoder -> OutputBuffer <---------------> Buffer
 *          queueInputBuffer    输入缓存区       编码器/解码器       输出缓存区     releaseOutputBuffer
 *          2.将数据放入缓存区                                                    4.释放处理后的数据占用的空间
 *  2.主要接口
 *      创建解码器(type为mime或name)
 *          public static MediaCodec createDecoderByType(String type)
 *      创建解码器(type为mime或name)
 *          public static MediaCodec createEncoderByType(String type)
 *      配置解码器和编码器(flag 0:解码器 1:编码器)
 *          public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags)
 *      启动解码器或编码器
 *          public final void start()
 *      获取输入队列的一个空闲索引(timeoutUs:最多等待时间， -1表示一直等待 单位:微妙us)
 *          public final int dequeInputBuffer(long timeoutUs)
 *      提醒解码器或编码器处理数据(index:dequeueInputBuffer方法的返回值)
 *          public final void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags)
 *      创建BufferInfo类，用于存储解码或编码后的缓存数据的格式信息
 *          public final static class BufferInfo
 *      获取输出队列的一个缓存区的索引，并将格式信息保存在info中(timeoutUs:最多等待时间 -1：一直等待 单位：us)
 *          public final int dequeueOutoutBuffer(BufferInfo info, long timeoutUs)
 *      获取输出队列的一个缓存区(index:dequeueOutputBuffer方法的返回值)
 *          public ByteBuffer getOutputBuffer(int index)
 *      清除index指向的缓存区中的数据
 *          public final void releaseOutputBuffer(int index, boolean render)
 *      结束解码或编码回话
 *          public final void stop()
 *      释放资源
 *          public final void release()
 */

#include "H264MediaCodecDecoder.h"

static AVPixelFormat G_HwPixFmt = AV_PIX_FMT_NONE;

static enum AVPixelFormat
getHWFormat(AVCodecContext *pCodecCtx, const enum AVPixelFormat *pPixelFmt) {
  const enum AVPixelFormat *pPixFmt;
  for (pPixFmt = pPixelFmt; *pPixFmt != -1; pPixFmt++) {
    if (*pPixFmt == G_HwPixFmt) {
      return *pPixFmt;
    }
  }

  return AV_PIX_FMT_NONE;
}

const AVCodec *H264MediaCodecDecoder::findCodec(DecoderParam &decoderParam) {
  auto pCodec = avcodec_find_decoder_by_name("h264_mediacodec");
  for (int i = 0;; ++i) {
    //查看硬件设备是否支持硬解码
    auto pHwConfig = avcodec_get_hw_config(pCodec, i);
    if (pHwConfig && (pHwConfig->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
        && (pHwConfig->device_type == AV_HWDEVICE_TYPE_MEDIACODEC)) {
      G_HwPixFmt = pHwConfig->pix_fmt;
      return pCodec;
    }
  }

  return nullptr;
}

int32_t
H264MediaCodecDecoder::initContextParam(AVCodecContext *pCodecCtx, DecoderParam &decoderParam) {
  int32_t ret = avcodec_parameters_to_context(pCodecCtx, decoderParam.pStream->codecpar);
  if (ret < SUCCESS) {
    return ret;
  }

  pCodecCtx->pkt_timebase = decoderParam.pStream->time_base;
  pCodecCtx->get_format = getHWFormat;
  //硬件解码器初始化
  AVBufferRef *pHWDeviceCtx = nullptr;
  ret = av_hwdevice_ctx_create(&pHWDeviceCtx, AV_HWDEVICE_TYPE_MEDIACODEC, nullptr, nullptr, 0);
  if (ret < SUCCESS) {
    return ret;
  }

  pCodecCtx->hw_device_ctx = av_buffer_ref(pHWDeviceCtx);
  return SUCCESS;
}

MediaType H264MediaCodecDecoder::getMediaType() {
  return MediaType_Video;
}

const char *H264MediaCodecDecoder::getClassName() {
  return "H264MediaCodecDecoder";
}

void H264MediaCodecDecoder::toString() {
  LOG_TEST("H264MediaCodecDecoder()")
}